package com.wolfking.jeesite.tool;

import com.google.common.collect.Lists;
import com.wolfking.jeesite.util.OsUtils;
import lombok.Setter;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import java.io.Closeable;
import java.io.IOException;
import java.util.Collection;
import java.util.Objects;

@Setter
public class CloseableRestTemplate extends RestTemplate implements Closeable {


    private Collection<Closeable> closeableCollection;

    @Override
    public void close() throws IOException {
        if (Objects.nonNull(closeableCollection)) {
            for (Closeable closeable : closeableCollection) {
                closeable.close();
            }
        }
    }

    @SneakyThrows
    public byte[] getForBytes(String url) {
        HttpHeaders headers = new HttpHeaders();
        //设置用流的方式获取图片
        headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_OCTET_STREAM));
        HttpEntity<String> entity = new HttpEntity<>(headers);
        //通过POST方式请求微信的接口
        return exchange(url, HttpMethod.GET, entity, byte[].class).getBody();
    }


    private CloseableRestTemplate() {

    }

    public static final PoolingHttpClientConnectionManager CONNECTION_MANAGER_INSTANCE = new PoolingHttpClientConnectionManager() {{
        setMaxTotal(100);
        //单host（可以理解为单域名）最大并发数，在依赖的外部系统数量较少时，可以配置和maxTotal一致。
        setDefaultMaxPerRoute(100);
        //这个写1，每次使用前validate，时间是1毫秒
        setValidateAfterInactivity(1);
    }};


    public static final CloseableHttpClient HTTPCLIENT_INSTANCE = HttpClients.custom()
            .setConnectionManager(CONNECTION_MANAGER_INSTANCE)
            .useSystemProperties()
            .setConnectionManagerShared(true)
            .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy() {
                @Override
                public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
                    long keepAlive = super.getKeepAliveDuration(response, context);
                    if (keepAlive == -1) {
                        keepAlive = 120 * 1000;
                    }
                    return keepAlive;
                }
            })
            .build();

    public static final CloseableRestTemplate INSTANCE = new CloseableRestTemplate() {{
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(HTTPCLIENT_INSTANCE);
        requestFactory.setConnectTimeout(OsUtils.winCondition(200, 1000));
        requestFactory.setReadTimeout(30000);
        requestFactory.setConnectionRequestTimeout(OsUtils.winCondition(200, 10000));
        setRequestFactory(requestFactory);
    }};

    public static RestTemplateBuilder builder() {
        return new RestTemplateBuilder();
    }

    public static class RestTemplateBuilder {

        private RestTemplateBuilder() {
        }


        private boolean defaultInstance;

        private HttpClient httpClient;

        private HttpClientConnectionManager connectionManager;

        private CredentialsProvider defaultCredentialsProvider;

        private int connectionManagerMaxTotal = 100;

        private int connectionManagerDefaultMaxPerRoute = 100;

        private int connectionManagerValidateAfterInactivity = 1;

        private boolean shareConnectionManager;

        private String username;

        private String password;

        private int connectTimeout = 5000;

        private int readTimeout = 30000;

        private int connectionRequestTimeout = 5000;

        private int keepAlive = 60;

        private ConnectionKeepAliveStrategy keepAliveStrategy;


        public RestTemplateBuilder httpClient(HttpClient httpClient) {
            this.httpClient = httpClient;
            return this;
        }

        public RestTemplateBuilder connectionManager(HttpClientConnectionManager connectionManager) {
            this.connectionManager = connectionManager;
            return this;
        }

        public RestTemplateBuilder username(String username) {
            this.username = username;
            return this;
        }

        public RestTemplateBuilder password(String password) {
            this.password = password;
            return this;
        }

        public RestTemplateBuilder connectTimeout(int connectTimeout) {
            this.connectTimeout = connectTimeout;
            return this;
        }

        public RestTemplateBuilder readTimeout(int readTimeout) {
            this.readTimeout = readTimeout;
            return this;
        }

        public RestTemplateBuilder connectionRequestTimeout(int connectionRequestTimeout) {
            this.connectionRequestTimeout = connectionRequestTimeout;
            return this;
        }

        public RestTemplateBuilder connectionManagerMaxTotal(int connectionManagerMaxTotal) {
            this.connectionManagerMaxTotal = connectionManagerMaxTotal;
            return this;
        }

        public RestTemplateBuilder connectionManagerDefaultMaxPerRoute(int connectionManagerDefaultMaxPerRoute) {
            this.connectionManagerDefaultMaxPerRoute = connectionManagerDefaultMaxPerRoute;
            return this;
        }

        public RestTemplateBuilder connectionManagerValidateAfterInactivity(int connectionManagerValidateAfterInactivity) {
            this.connectionManagerValidateAfterInactivity = connectionManagerValidateAfterInactivity;
            return this;
        }

        public RestTemplateBuilder defaultCredentialsProvider(CredentialsProvider defaultCredentialsProvider) {
            this.defaultCredentialsProvider = defaultCredentialsProvider;
            return this;
        }

        public RestTemplateBuilder shareConnectionManager(boolean shareConnectionManager) {
            this.shareConnectionManager = shareConnectionManager;
            return this;
        }

        public RestTemplateBuilder keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy) {
            this.keepAliveStrategy = keepAliveStrategy;
            return this;
        }

        public RestTemplateBuilder keepAlive(int keepAlive) {
            this.keepAlive = keepAlive;
            return this;
        }

        public RestTemplateBuilder defaultInstance() {
            this.defaultInstance = true;
            return this;
        }

        public CloseableRestTemplate build() {
            Collection<Closeable> closeList = Lists.newArrayList();
            if (this.httpClient == HTTPCLIENT_INSTANCE) {
                this.defaultInstance = true;
            }
            if (this.defaultInstance) {
                return INSTANCE;
            } else {
                if (Objects.isNull(this.httpClient)) {
                    if (Objects.isNull(this.connectionManager)) {
                        this.connectionManager = new PoolingHttpClientConnectionManager() {{
                            setMaxTotal(RestTemplateBuilder.this.connectionManagerMaxTotal);
                            //单host（可以理解为单域名）最大并发数，在依赖的外部系统数量较少时，可以配置和maxTotal一致。
                            setDefaultMaxPerRoute(RestTemplateBuilder.this.connectionManagerDefaultMaxPerRoute);
                            //这个写1，每次使用前validate，时间是1毫秒
                            setValidateAfterInactivity(RestTemplateBuilder.this.connectionManagerValidateAfterInactivity);
                        }};
                        this.shareConnectionManager = false;
                    } else {
                        if (this.connectionManager == CONNECTION_MANAGER_INSTANCE) {
                            shareConnectionManager = true;
                        }
                    }
                    if (!this.shareConnectionManager) {
                        closeList.add(() -> RestTemplateBuilder.this.connectionManager.shutdown());
                    }
                    HttpClientBuilder custom = HttpClients.custom();
                    custom.setConnectionManager(connectionManager)
                            .useSystemProperties()
                            .setConnectionManagerShared(this.shareConnectionManager);
                    if (Objects.nonNull(this.keepAliveStrategy)) {
                        custom.setKeepAliveStrategy(this.keepAliveStrategy);
                    } else if (this.keepAlive > 0) {
                        custom.setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy() {
                            @Override
                            public long getKeepAliveDuration(final HttpResponse response, final HttpContext context) {
                                long keepAlive = super.getKeepAliveDuration(response, context);
                                if (keepAlive == -1) {
                                    keepAlive = RestTemplateBuilder.this.keepAlive;
                                }
                                return keepAlive;
                            }
                        });
                    }
                    if (Objects.nonNull(this.defaultCredentialsProvider)) {
                        custom.setDefaultCredentialsProvider(defaultCredentialsProvider);
                    } else if (StringUtils.isNotBlank(username) && StringUtils.isNoneBlank(password)) {
                        BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
                        this.defaultCredentialsProvider = credentialsProvider;
                        custom.setDefaultCredentialsProvider(credentialsProvider);
                    }
                    CloseableHttpClient build = custom.build();
                    this.httpClient = build;
                    closeList.add(build);
                }
                return getCloseableRestTemplate(closeList);
            }
        }

        private CloseableRestTemplate getCloseableRestTemplate(Collection<Closeable> closeList) {
            HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(this.httpClient);
            requestFactory.setConnectTimeout(this.connectTimeout);
            requestFactory.setReadTimeout(this.readTimeout);
            requestFactory.setConnectionRequestTimeout(this.connectionRequestTimeout);
            CloseableRestTemplate closeableRestTemplate = new CloseableRestTemplate();
            closeableRestTemplate.setRequestFactory(requestFactory);
            closeableRestTemplate.setCloseableCollection(closeList);
            return closeableRestTemplate;
        }
    }
}
